﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once

#include "IBody.h"
#include "BodyTopologyOperateData.h"
#include "GeometryErrorCode.h"
#include "BlendFixOptions.h"

namespace gcmp
{
    class IPlane;
    class ISurface;
    class IPolygon;
    class IFace;
    class Vector3d;
    class IGraphicsBRepBody;
    enum class GeometryErrorCode;
    struct WireBodyOffsetOptions;

    class IBodyTopologyOperate
    {
    public:
        virtual ~IBodyTopologyOperate() {}

         /// \brief 输入两个Body, tool和blank 交集运算
         /// \param tool Plane    切面
         /// \param blank Body    被切的体
         /// \param OwnerPtr<IBody> &positiveBody 平面法向正向的体对象
         /// \param OwnerPtr<IBody> &negativeBody 平面法向反向的体对象
         /// \param double tolerance              容差，默认0.1
         /// \return 返回true表示计算成功，返回false表示计算失败
        virtual bool SplitBody(const IPlane* tool,  const IBody* blank, OwnerPtr<IBody> &positiveBody,OwnerPtr<IBody> &negativeBody , double tolerance)const = 0;
        
        /// \brief  平面分割体，返回分割后的平面截面和平面截面上每条边对应被分割体上面的索引
        /// \param pToolPlane 分割平面
        /// \param pBodyToSplit 被分割的体
        /// \param opPolygon 分割后的平面截面
        /// \param pFaceIndexVec 二维数组，第一维代表平面截面opPolygon中LOOP的索引，第二维代表LOOP中边的索引，数组中存放的值代表分割体pBodyToSplit上面的索引。以pFaceIndexVec[0][1]==3为例，表示所在Polygon中，存在索引为0的loop，该loop中索引为0的边在pBodyToSplit的拓扑值为3。pFaceIndexVec可以为空指针nullptr，如果传入的为空指针则代表不做计算
        /// \return 返回true表示计算成功，返回false表示计算失败
        virtual bool SliceBody(const IPlane* pToolPlane, const IBody* pBodyToSplit, OwnerPtr<IPolygon>& opPolygon, std::vector<std::vector<int>>* pFaceIndexVec)const = 0;

         /// \brief 
         /// \param bodyToSplit  被切的体(内部带有拓扑命名信息)
         /// \param toolPlane    切面
         /// \param opResultNegativeGBody 平面法向负向的体对象(带有拓扑命名信息)
         /// \param boolFaceSourceIndices opResultNegativeGBody中每个面的来源体序号和面序号
         /// \param boolEdgeSourceIndices opResultNegativeGBody中每个边的来源体序号和边序号
         /// \param double tolerance      容差，默认0.1
         /// \param ppResultPositiveGBody 平面法向正向的体对象(带有拓扑命名信息)
         /// \param pPositiveFaceSourceData ppResultPositiveGBody中每个面的来源体序号和面序号
         /// \param pPositiveEdgeSourceData ppResultPositiveGBody中每个边的来源体序号和边序号
         /// \return EnPlaneSplitBodyResult   面裁体结果
        virtual EnPlaneSplitBodyResult SplitBody(const IBody* bodyToSplit,  const IPlane* toolPlane, OwnerPtr<IBody>& opResultNegativeGBody,
            std::vector<BoolResultFaceSourceData>& boolFaceSourceIndices,
            std::vector<BoolResultEdgeSourceData> &boolEdgeSourceIndices,
            double tolerance,
            OwnerPtr<IBody>* ppResultPositiveGBody,
            std::vector<BoolResultFaceSourceData>* pPositiveFaceSourceData,
            std::vector<BoolResultEdgeSourceData>* pPositiveEdgeSourceData)const = 0;

        GCMP_OBSOLETE_API(2022 / 04 / 01, IBodyTopologyOperate::SplitBody(const IBody*, const IPlane*, OwnerPtr<IBody>&, std::vector<BoolResultFaceSourceData>&, std::vector<BoolResultEdgeSourceData>&, double
            , OwnerPtr<IBody>*, std::vector<BoolResultFaceSourceData>*, std::vector<BoolResultEdgeSourceData>*)const)
            virtual EnPlaneSplitBodyResult SplitBody(const IBody* bodyToSplit, const IPlane* toolPlane, OwnerPtr<IBody>& resultGBody,
                std::vector<BoolResultFaceSourceData>& boolFaceSourceIndices, std::vector<BoolResultEdgeSourceData> &boolEdgeSourceIndices, double tolerance) const = 0;
        
        /// \brief 平面分割体，返回分割后的平面截面CFace（截面坐标系由分割平面确定）构成的Shell Body，这个Shell Body只有一个Face
        /// \param const IPlane* tool 分割平面
        /// \param const IBody* blank 待分割的体
        /// \return 分割后的平面截面Face（截面坐标系由分割平面确定）构成的Shell Body
        virtual OwnerPtr<IBody> SplitBodyAndGetSectionShellBody(const IPlane* tool, const IBody* blank)const = 0;

        
        /// \brief 平面分割壳，返回分割后的两个壳对象
        /// \param const IPlane* tool 分割平面
        /// \param const IBody* shell 待分割的壳
        /// \param OwnerPtr<IBody> &positiveShell 分割的结果壳{平面法向正向的壳对象}
        /// \param OwnerPtr<IBody> &negativeShell 分割的结果壳{平面法向反向的壳对象}
        /// \return 计算成功返回true，结果体为nullptr表示没有切开;计算失败返回false
        virtual bool SplitShell(const IPlane* tool,  const IBody* shell, OwnerPtr<IBody> &positiveShell,OwnerPtr<IBody> &negativeShell) const = 0;

        /// \brief 壳分割体
        /// \param const IBody* shell 壳, 可以不封闭，根据法向判断内外
        /// \param const IBody* sourceBody 待分割的体
        /// \paramOwnerPtr<IBody> &positiveBody 分割的结果体{壳外的体对象}
        /// \paramOwnerPtr<IBody> &negativeBody 分割的结果体{壳内的体对象}
        /// \return 计算成功返回true，结果体为nullptr表示没有切开;计算失败返回false
        virtual bool ShellSplitBody(const IBody* shell, const IBody* sourceBody, OwnerPtr<IBody> &positiveBody, OwnerPtr<IBody> &negativeBody) const = 0;

        
        /// \brief 壳扣减体
        /// \param const IBody* shell 壳, 被扣减的壳
        /// \param const IBody* sourceBody 扣减体
        /// \paramOwnerPtr<IBody> &result  结果体（还是壳）
        /// \return 计算成功返回true;计算失败返回false
        virtual bool ShellSubstractBody(const IBody* shell, const IBody* pBody, OwnerPtr<IBody> &result) const = 0;

        
        /// \brief 壳交体
        /// \param const IBody* shell 壳
        /// \param const IBody* sourceBody 体
        /// \paramOwnerPtr<IBody> &result  结果体（还是壳）
        /// \return 有交返回true;否则返回false
        virtual bool ShellIntersectBody(const IBody* shell, const IBody* pBody, OwnerPtr<IBody> &result) const = 0;
                
        /// \brief 壳分割壳
        /// \param const IBody* shell 壳, 可以不封闭，根据法向判断内外
        /// \param const IBody* sourceShell 待分割的壳
        /// \param OwnerPtr<IBody> &positiveBody 分割的结果体{壳外的体对象}
        /// \param OwnerPtr<IBody> &negativeBody 分割的结果体{壳内的体对象}
        /// \return 计算成功返回true，结果壳为nullptr表示没有切开;计算失败返回false
        virtual bool ShellSplitShell(const IBody* shell, const IBody* sourceShell, OwnerPtr<IBody> &positiveShell, OwnerPtr<IBody> &negativeShell) const = 0;
        /*!
        *@brief 壳扣壳
        *@param[in] const IBody* shell1 壳1
        *@param[in] const IBody* shell2 壳2
        *@return 两壳的并
        */
        virtual OwnerPtr<IBody> ShellSubtractShell(const IBody* shell1, const IBody* shell2) const = 0;
        /*!
        *@brief 壳交壳
        *@param[in] const IBody* shell1 壳1
        *@param[in] const IBody* shell2 壳2
        *@return 两壳的并
        */
        virtual OwnerPtr<IBody> ShellIntersectShell(const IBody* shell1, const IBody* shell2)const = 0;
        /// \brief 壳并壳
        /// \param const IBody* shell1 壳1
        /// \param const IBody* shell2 壳2
        /// \return 两壳的并
        virtual OwnerPtr<IBody> ShellUniteShell(const IBody* shell1, const IBody* shell2) const = 0;

        /// \brief 分割体上的一条边
        /// \param const IBody* pBody 体
        /// \param int underlyingEdgeIndex 分割边的序号
        /// \param Vector3d point 分割边上的分割点
        /// \param std::multimap<int,int>& sourceToTargetEdgeIndexMap 分割前后体上边的映射
        /// \return 边分割后的体
        virtual OwnerPtr<IBody> SplitEdge(const IBody* pBody, int underlyingEdgeIndex,Vector3d point,
                                            std::multimap<int,int>& sourceToTargetEdgeIndexMap)const = 0;

        /// \brief 构造布尔体（包含交、并、差）
        /// \param const IBody *pFirstBody                              第一个体, 布尔差时为被扣减体(blank body)
        /// \param const IBody *pSecondBody                              第二个体, 布尔差时为扣减体(tool body)
        /// \param BodyBooleanOperation booleanOperation                布尔操作类型
        /// \param tolerance                                             容差,默认0.1
        /// \return 布尔体
        virtual OwnerPtr<IBody> BoolOperate(
            const IBody *pFirstBody,
            const IBody *pSecondBody,
            BodyBooleanOperation booleanOperation,
            double tolerance
        )const = 0;

        /// \brief 构造布尔体（包含交、并、差）,返回Face,Edge布尔运算前后映射关系
        /// \param const IBody *pFirstBody                              第一个体, 布尔差时为被扣减体(blank body)
        /// \param const IBody *pSecondBody                              第二个体, 布尔差时为扣减体(tool body)
        /// \param BodyBooleanOperation booleanOperation                布尔操作类型
        /// \param boolFaceSourceIndices                                 面的映射关系
        /// \param boolEdgeSourceIndices                                 边的映射关系
        /// \param tolerance                                             容差,默认0.1
        /// \return 布尔体
        virtual OwnerPtr<IBody> BoolOperate(
            const IBody *pFirstBody, 
            const IBody *pSecondBody, 
            BodyBooleanOperation booleanOperation,
            std::vector<BoolResultFaceSourceData>& boolFaceSourceIndices,
            std::vector<BoolResultEdgeSourceData>& boolEdgeSourceIndices,
            double tolerance
            )const = 0 ;

        /// \brief 构造布尔体（包含交、并、差）,返回布尔操作结果类型、结果体和Face,Edge布尔运算前后映射关系
        /// \param const IBody *pFirstBody                                        第一个体, 布尔差时为被扣减体(blank body)
        /// \param const IBody *pSecondBody                                       第二个体, 布尔差时为扣减体(tool body)
        /// \param BodyBooleanOperation booleanOperation                          布尔操作类型
        /// \param std::vector<BoolResultFaceSourceData>& FaceSourceIndices       面的映射关系
        /// \param std::vector<BoolResultEdgeSourceData>& EdgeSourceIndices       边的映射关系
        /// \param OwnerPtr<IBody>& resultBody                                    布尔操作后的结果体
        /// \param double tolerance                                               容差
        /// \return BoolOperateErrorStatus                                        布尔操作结果类型
        virtual BoolOperateErrorStatus BoolOperate(
            const IBody* pFirstBody,
            const IBody* pSecondBody,
            BodyBooleanOperation booleanOperation,
            std::vector<BoolResultFaceSourceData>& resultFaceSourceData,
            std::vector<BoolResultEdgeSourceData>& resultEdgeSourceData,
            OwnerPtr<IBody>& resultBody,
            double tolerance
        )  const = 0;
        
        /// \brief 计算两个体的消隐线
        /// \param const IBody* pBody1 第一个体
        /// \param const IBody* pBody2 第二个体
        /// \param OwnerPtr<IBody>& opNewBody1 第一个体经消隐操作生成的体
        /// \param OwnerPtr<IBody>& opNewBody2 第二个体经消隐操作生成的体
        /// \param std::vector<HiddenEdgeSourceData>& edgeSourceStructs1 第一个体消隐操作前后，边序号的来源边，是否是split edge
        /// \param std::vector<HiddenEdgeSourceData>& edgeSourceStructs2 第二个体消隐操作前后，边序号的来源边，是否是split edge
        /// \paramstd::map<int,int>& hiddenEdgeIndexPairs 第一个体与第二个体体消隐边序号的对应
        virtual bool CalculateHiddenEdges(const IBody* pBody1,const IBody* pBody2,
                                          OwnerPtr<IBody>& opNewBody1,OwnerPtr<IBody>& opNewBody2,
                                          std::vector<HiddenEdgeSourceData>& edgeSourceStructs1,
                                          std::vector<HiddenEdgeSourceData>& edgeSourceStructs2,
                                          std::map<int,int>& hiddenEdgeIndexPairs) const = 0;

        /// \brief 计算两个体的消隐线, 直接修改输入体，内部不拷贝体
        /// \param IBody* pBody1 第一个体
        /// \param IBody* pBody2 第二个体
        /// \param std::vector<HiddenEdgeSourceData>& edgeSourceStructs1 第一个体消隐操作前后，边序号的来源边，是否是split edge
        /// \param std::vector<HiddenEdgeSourceData>& edgeSourceStructs2 第二个体消隐操作前后，边序号的来源边，是否是split edge
        /// \paramstd::map<int,int>& hiddenEdgeIndexPairs 第一个体与第二个体体消隐边序号的对应
        virtual bool CalculateHiddenEdges(IBody* pBody1, IBody* pBody2,
            std::vector<HiddenEdgeSourceData>& edgeSourceStructs1,
            std::vector<HiddenEdgeSourceData>& edgeSourceStructs2,
            std::map<int, int>& hiddenEdgeIndexPairs) const = 0;

        
        /// \brief 计算墙墙X型连接的消隐线
        /// \param const IBody* pCuttingBody 墙墙连接的切体
        /// \param const IBody* pCuttedBody 墙墙连接的被切体
        /// \param OwnerPtr<IBody>& opNewCuttingBody 经消隐操作生成的切体
        /// \param std::vector<HiddenEdgeSourceData>& edgeSourceStructs 切体的消隐操作前后，边序号的来源边，是否是split edge
        /// \paramstd::vector<int>& hiddenEdgesInFormerBody 切体中需要消隐边的序号
        virtual bool CalculateWallCrossedJoinHiddenEdges(const IBody* pCuttingBody,const IBody* pCuttedBody,
                                                         OwnerPtr<IBody>& opNewCuttingBody,
                                                         std::vector<HiddenEdgeSourceData>& edgeSourceStructs,
                                                         std::vector<int>& hiddenEdgesInCuttingBody) const = 0;

        
        /// \brief 检测接口FitBodyToTargetedFaceWithoutTopoChange输入数据的有效性，如有疑问，请联系zhujb-a
        /// \param const ISurface* pTargetedSurface                      目标曲面
        /// \param const IBody* pOriginalBody,                            原始体
        /// \param const IFace* pTractiveFaceOnOriginalBody               体上指定的牵引面
        /// \param bool bIsExtendedFirst                                   是否延伸端优先
        /// \return 数据有效返回true,无效返回false
        virtual bool IsValidForFitBodyToTargetedFaceWithoutTopoChange(
            const ISurface* pTargetedSurface,
            const IBody* pOriginalBody,
            const IFace* pTractiveFaceOnOriginalBody,
            bool bIsExtendedFirst
        ) const = 0;

        
        /// \brief 在体上指定牵引面，将体贴合到目标面，以实现体的延伸或裁剪
        ///     算法使用：1、输入的体上的所有面面和目标面的面类型只能为平面或者圆柱面；
        ///              2、目标面（延伸后目标面）必须与输入的体相交或者与输入的体延牵引面的延伸段相交；
        ///              3、用户保证贴合前后体的拓扑不变，即体上Face和Edge数量及相互位置关系不变，否则算法报错,不对输入的体做修改。
        /// \param const ISurface* pTargetedSurface                      目标曲面
        /// \param IBody* pOriginalBody,                              原始体
        /// \param const IFace* pTractiveFaceOnOriginalBody                     体上指定的牵引面
        /// \param bool bIsExtendedFirst                                   是否延伸端优先
        /// \return 计算成功返回true,失败返回false(返回false时，体可能变成无效的体，需要调用者自行回滚)
        virtual bool FitBodyToTargetedFaceWithoutTopoChange(
            const ISurface* pTargetedSurface,
            IBody* pOriginalBody,
            const IFace* pTractiveFaceOnOriginalBody,
            bool bIsExtendedFirst
        ) const = 0;

        /// \brief 在体上指定牵引面，将体贴合到目标面，以实现体的延伸或裁剪
        ///     算法使用：1、输入的体上的所有面面和目标面的面类型只能为平面或者圆柱面；
        ///              2、目标面（只能是平面）必须与输入的体相交或者与输入的体延牵引面的延伸段相交；
        ///              3、用户保证贴合前后体的拓扑不变，即体上Face和Edge数量及相互位置关系不变，否则算法报错,不对输入的体做修改。
        /// \param pTargetPlane                  目标平面
        /// \param pOriginalBody,                原始体
        /// \param pTractiveFaceOnOriginalBody   体上指定的牵引面
        /// \return 计算成功返回true,失败返回false(返回false时，体可能变成无效的体，需要调用者自行回滚)
        virtual bool FitBodyToTargetPlaneAlongVAxisWithoutTopoChange(
            const IPlane* pTargetPlane,
            IBody* pOriginalBody,
            const IFace* pTractiveFaceOnOriginalBody
        ) const = 0;

        /// \brief 合并邻接的重合面
        /// \param OwnerPtr<IBody> &opBody      待合并面的体
        /// \return 计算成功返回true,失败返回false
        virtual bool MergeCoincideFace(OwnerPtr<IBody> &opBody) const = 0;

        /// \brief  合并体
        /// \param OwnerPtr<IBody> &opDestGody                                合并到opDestGody
        /// \param const std::vector<const IBody*>& pBodiesToBeMerge          被合并的体
        /// \return 计算成功返回true,失败返回false
        virtual bool MergeBody(OwnerPtr<IBody> &opDestGody, const std::vector<const IBody*>& pBodiesToBeMerge) const = 0;

        /// \brief   平移Brep体给定面。该面及面所在的Brep体可能被直接修改
       /// 
       /// \param  pFace      待平移的面，要求其所在的Brep体是有效的
       /// \param  rVector    平移向量
       /// \param[out]  resultBody   生成的Brep体
       /// \return 错误码
        virtual GeometryErrorCode MoveFace(const IFace* pFace, const Vector3d& rVector, OwnerPtr<IBody>& resultBody) const = 0;

        /// \brief  偏移Brep体给定面
        /// \param[in] pFace  待偏移的面，要求其所在的Brep体是有效的
        /// \param[in] offset  偏移距离（可为负值，表示按面法向的负向偏移）
        /// \param[out] resultBody  结果Brep体
        /// \return 错误码
        virtual GeometryErrorCode OffsetFace(const IFace* pFace, double offset, OwnerPtr<IBody>& opOffsetBody) const = 0;

        /// \brief  WireBody在指定体上做偏移
        /// \param[in] wireBody  需要偏移的共面的wirebody
        /// \param[in] supportBody  WireBody偏移前后所在的体，类型为壳体或实体。可为空
        /// \param[in] option  偏移选项： 
        ///                     偏移距离:大于0，正向偏移，小于0，负向偏移。（正向：平面法向与曲线切向的叉乘为正向偏移）
        ///                     平面法向:wirebody平面法向
        ///                     连接方式：默认延长求交
        ///                     容差
        /// \param[out] opOffsetBody  输出结果体WireBody
        /// \return 错误码
        virtual GeometryErrorCode OffsetWireBody(const IBody* wireBody, const IBody* supportBody, const WireBodyOffsetOptions &option, OwnerPtr<IBody>& opOffsetBody) const = 0;

        /// \brief  合并Brep体的Faces
        /// \param  pBody 待合并的体
        /// \param  dDistEpsilon  容差含义： 距离容差，一般建议使用默认角度容差
        /// \param  dAngleEpsilon 容差含义： 角度容差；一般建议使用默认角度容差
        /// \return 如果面有合并，则返回 true. 没有变化返回 false`
        virtual bool MergeFaces(IBody* pBody, double dDistEpsilon, double dAngleEpsilon) const = 0;

        /// \brief  合并Brep体的Edges
        /// \param  pBody 待合并的体
        /// \param  dDistEpsilon     容差含义： 距离容差，一般建议使用默认角度容差
        /// \param  dAngleEpsilon    容差含义： 角度容差；一般建议使用默认角度容差
        /// \return 如果边有合并，则返回 true. 没有变化返回 false
        virtual bool MergeEdges(IBody* pBody, double dDistEpsilon, double dAngleEpsilon) const = 0;

        /// \brief  将给定Sheet壳体的面补全，生成Solid实体
        /// \param[in] pSheetBody  待补面的壳体
        /// \param[out] opFilledBody  填充的Solid实体
        /// \param[in] dDistEpsilon  缝合容差
        /// \return 错误码
        virtual GeometryErrorCode FillFace(const IBody* pSheetBody, OwnerPtr<IBody>& opFilledBody, double dDistEpsilon) const = 0;

        /// \brief  常半径倒圆角
        /// \param[in] edgeLists  用户输入的圆角边列表，所有的edges需要在同一个体上
        /// \param[in] options  用户指定的圆角功能选项（该选项暂不支持，先占位，默认构造即可）
        /// \param[in] radius  圆角半径
        /// \param[out] resultInfo  倒角的结果信息
        ///             resultInfo.ResultBody  新生成的带圆角的体
        ///             后面用于扩展一些错误信息，拓扑追踪等
        /// \return 错误码
        virtual BlendErrorCode FixConstantBlends(const std::vector<const IEdge*>& edgeLists, gcmp::BlendFixOptions& options, double radius, gcmp::BlendResultInfo& resultInfo) const = 0;

        /// \brief  通过距离倒斜角
        ///         目前仅支持等距倒斜角，即range1 等于 range2，faceLists输入为空列表
        /// \param[in] edgeLists  用户输入的倒角边列表，所有的edges需要在同一个体上
        /// \param[in] faceLists  不等距倒角的range1所在的面列表,等距倒角可输入空列表
        /// \param[in] range1  倒角距离 1，等距倒角range1等于range2，不等距倒角faceLists不能为空
        /// \param[in] range2  倒角距离 2
        /// \param[in] options  用户指定的倒角功能选项（该选项暂不支持，先占位，默认构造即可）
        /// \param[out] resultInfo  倒角的结果信息
        ///             resultInfo.ResultBody  新生成的倒角的体
        ///             后面用于扩展一些错误信息，拓扑追踪等
        /// \return 错误码
        virtual BlendErrorCode FixChamferBlends(const std::vector<const IEdge*>& edgeLists, const std::vector<const IFace*>& faceLists, double range1, double range2, gcmp::BlendFixOptions& options, gcmp::BlendResultInfo& resultInfo) const = 0;

        /// \brief  开启FindCoinEdges时的RTree加速缓存
        /// \param
        /// \return
        /// \remark 由于接口是针对单组body的，所以采用标记位加缓存的方式，不然改动太大
        virtual void BeginRTreeCacheForFindCoinEdges() const = 0;
        /// \brief  结束FindCoinEdges时的RTree加速缓存
        /// \param
        /// \return
        virtual void EndRTreeCacheForFindCoinEdges() const = 0;

        virtual void RemoveRTreeCacheForFindCoinEdges(IGraphicsBRepBody* pBody) const = 0;
        /*!
        * @brief   印刻：用给定Wire分割给定面。该面及面所在的Brep体将被直接修改
        * @param[in]  pFace                待分割的面，要求其所在的Brep体是有效的
        * @param[in]  pSplitWire           用于分割的Wire
        * @param[in]  pBody                分割后的新的body
        * @param[in]  dDistEpsilon         求交距离容差
        * @return    如果分割成功，则返回 true. 分割失败 false
        */
        virtual GeometryErrorCode Imprint(IFace* pFace,const IBody* pSplitWire, OwnerPtr<IBody>& pBody, double dDistEpsilon) const = 0;
    };
}
